#include<stdio.h>
#include<sys/types.h>
#include<sys/ipc.h>
#include<sys/sem.h>
#include<stdlib.h>
#include<unistd.h>
#include<wait.h>

#include <string.h>

#include <fcntl.h>

#include <sys/shm.h>
#include <sys/msg.h>

/* -------- DEFINE ----------------- */
#define MAX 100
#define PROJ_ID_SEMAFORO 'S'
#define PROJ_ID_MEMORIA_COMPARTIDA 'M'
#define PROJ_ID_COLA_MENSAJE 'C'

/* ESTRUCTURA */
union semun {
	int val;                    /* value for SETVAL */
	struct semid_ds *buf;       /* buffer for IPC_STAT, IPC_SET */
	unsigned short int *array;  /* array for GETALL, SETALL */
	struct seminfo *__buf;      /* buffer for IPC_INFO */
} inicio;


struct un_msgbuf {
  long mtype;
  char mtext[MAX+1];
}  mq_test_buf;


/* -----------------VARIABLES GLOBALES -----------------------------------------*/
/* -- VARIALBLES RELACIONADAS CON SEMÁFOROS --- */
struct sembuf down;
struct sembuf up;

key_t llave_semaforo;
int id_semaforo;

/* -- VARIABLES RELACIONADAS CON MEMORIA COMPARTIDA -- */
key_t llave_memoria_compartida;
int id_memoria_compartida;

char *ctl_memoria_compartida;


/* -- VARIABLES RELACIONADAS CON COLA DE MENSAJE ... */


/* Prototipos de funciones */
void operacion_hijo();
void operacion_padre();

/*-------------------------------------------------------------------------------------------------*/

int main(int argc, char* argv[])
{
	/*-------------------------------------------------------------------------------------------------*/
	int pid_hijo;



	/* Inicia la estructura DOWN */
	down.sem_num = 0;
	down.sem_op = -1;
	down.sem_flg = SEM_UNDO;

	/* Inicia la estructura UP */
	up.sem_num = 0;
	up.sem_op =  1;
	up.sem_flg = SEM_UNDO;


	/* CREACIÓN DEL SEMÁFORO */
	llave_semaforo = ftok("/tmp/fichero_semaforo",PROJ_ID_SEMAFORO);
	id_semaforo = semget(llave_semaforo,1,IPC_CREAT | 0666);



	if(id_semaforo == -1)
	{
		perror("Error al crear los semáforos");
		exit(3);
	}

	inicio.val = 1;

	if (semctl(id_semaforo, 0, SETVAL, inicio) == -1)
	{
		perror("inicializando semaforo");
		semctl(id_semaforo,0, IPC_RMID);
		exit(4);
	}

	/* Creación de una llave para la memoria compartida.*/
	llave_memoria_compartida = ftok("/temp/fichero_memoria_compartida",PROJ_ID_MEMORIA_COMPARTIDA);
	id_memoria_compartida = shmget(llave_memoria_compartida,MAX,IPC_CREAT | 0666);
	ctl_memoria_compartida = shmat(id_memoria_compartida,0,0);

	/* Crear un proceso hijo */

		if((pid_hijo=fork())==-1){
			perror("Error en fork");
			exit(1);
		}
		else if (pid_hijo==0){
			/* Hijo */
			operacion_hijo();
		} else
		{
			operacion_padre();
		}

		/* -------------------------------------------*/
		shmdt(ctl_memoria_compartida);
		semctl(id_memoria_compartida,0, IPC_RMID);
		semctl(id_semaforo,0, IPC_RMID);

		return 0;
}

void operacion_padre()
{
	int indice;
	int tamano = 0;
	char caracter;
	char nombre_fichero[100] = "inputdata";
	char buffer[MAX];
	int fd;
	int nbytes;
	/*Abrir el fichero */
	fd = open(nombre_fichero,O_RDONLY);
	if(fd == -1)
	{
		perror("Error de apertura de fichero de datos.\n");
		exit(-1);
	}



	while(1)
	{
		sleep(1);
		/* Leer datos desde el fichero */
		nbytes = read(fd,buffer,MAX);
		if(nbytes == -1)
		{
			perror("Error de lectura del fichero.\n");
			exit(-1);
		}
		else if(nbytes == 0) break;
		else
		{
			semop(id_semaforo, &down, 1);
			strcpy(ctl_memoria_compartida,buffer);
			semop(id_semaforo, &up, 1);
		}
	}

	/* No hay más datos para leer */
	ctl_memoria_compartida[0] = '\0';
	/*Cerra el fichero.*/
	close(fd);
	wait(NULL);
	printf("Han muerto el hijo.\n");

}

/* --------------------------------------------------- */
void operacion_hijo()
{
	char buffer[MAX];
	int sin_dato = 0;
	/* Variables para cola de mensaje */
	key_t llave_cola_mensaje;
	int id_cola_mensaje;

	llave_cola_mensaje = ftok("/tmp/fichero_cola_mensaje",PROJ_ID_COLA_MENSAJE);
	id_cola_mensaje = msgget(llave_cola_mensaje,IPC_CREAT|0666);

	if (id_cola_mensaje == -1)
	{
		printf("Error creando o conectando a la cola de mensajes\n");
		exit(-1);
	}


	while(1)
	{
		sleep(1);
		semop(id_semaforo, &down, 1);

		mq_test_buf.mtype = 123;
		strcpy(mq_test_buf.mtext,buffer);
		if (msgsnd(id_cola_mensaje, (struct msgbuf *)&mq_test_buf, MAX+1,0) == -1)
		{ // El +1 por el \0 del sprintf
			perror("envio");
			printf("Error enviando mensaje\n");
			exit(-1);
		}


		strcpy(buffer,ctl_memoria_compartida);
		semop(id_semaforo, &up, 1);

		if(buffer[0] == '\0')
		{
			printf("No hay más datos.\n");
			sin_dato = 1;
			break;
		}
		else
		{
			//printf("Hijo => %s\n",buffer);
		}

		if(sin_dato == 1) break;
		else continue;
	}

	/* Liberar cola mensaje */
	msgctl(id_cola_mensaje,IPC_RMID,NULL);
}
